package com.szwistar.common.comm.logserver;

import com.szwistar.common.os.TimerEx;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

class LogClientHandler implements IoHandler {
	static final Logger LOG = LoggerFactory.getLogger("Default");

	MinaLogServerAppender server;

	public LogClientHandler(MinaLogServerAppender server) {
		this.server = server;
	}

	@Override
	public void sessionCreated(IoSession session) {
	}

	@Override
	public void sessionOpened(IoSession session) {
		LOG.debug("新LOG客户端接入({}): {}", session.getId(), session.getRemoteAddress().toString());
		server.addClient(session);
	}

	@Override
	public void sessionClosed(IoSession session) {
		LOG.debug("LOG客户端断开连接({}): {}", session.getId(), session.getRemoteAddress().toString());
		server.removeClient(session);
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status) {
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) {
		LOG.debug("LOG客户端断开连接({}): {}", session.getId(), session.getRemoteAddress().toString());
		server.removeClient(session);
	}

	@Override
	public void messageSent(IoSession session, Object message) {
	}

	@Override
	public void messageReceived(IoSession session, Object message) {
	}
}

public class MinaLogServerAppender extends AppenderSkeleton {
	static final Logger LOG = LoggerFactory.getLogger(MinaLogServerAppender.class);

    // 编码格式(默认为GB2312)
    String encode = "GB2312"; // "UTF-8";

    // 监听地址
    String address = "*";
    int port = 9406;

    // 最大使用的 IO 处理线程数量
    int maxIoProcessThreadCount = 1;

    // 接收者
    IoAcceptor acceptor;

    // 客户端
    List<IoSession> clients = new ArrayList<IoSession>();

    // 是否使能
    boolean enable = true;

	public void setEncode(String encode) {
		this.encode = encode;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public void setMaxIoProcessThreadCount(int maxIoProcessThreadCount) {
		this.maxIoProcessThreadCount = maxIoProcessThreadCount;
	}

	/**
     * 添加一个新的客户端
     * @param client
     */
    public void addClient(IoSession client) {
    	clients.add(client);
    }

    /**
     * 移除一个客户端
     */
    public void removeClient(IoSession client) {
    	client.close(true);
    	clients.remove(client);
    }

	protected void start() {
        // 在安卓系统中，不允许在主线程创建连接，必须放到一个线程中去执行
        try {
            // 接收者
            //new NioSocketAcceptor(Runtime.getRuntime().availableProcessors() + 1, Executors.newCachedThreadPool())
            acceptor = new NioSocketAcceptor(maxIoProcessThreadCount);

            // 设置编码器
            acceptor.getFilterChain().addLast("codec",
                    new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName(encode))));

            // 设置Handler
            acceptor.setHandler(new LogClientHandler(MinaLogServerAppender.this));

            // 绑定端口,启动服务，并开始处理远程客户端请求
            if((address == null) || address.equals("*")) {
            	acceptor.bind(new InetSocketAddress(port));
            } else {
            	acceptor.bind(new InetSocketAddress(address, port));
            }
        } catch(Exception e) {
            LOG.debug("启动 LOG 服务器 {}:{} 失败！{}", address, port, e);
            enable = false;
            return;
        }
        LOG.debug("启动 LOG 服务器 {}:{} 成功！", address, port);
	}

	@Override
	protected void append(LoggingEvent le) {
		if((acceptor == null) && enable) {
			start();
		}

		String log = getLayout().format(le);

		// 向每个客户端发送一遍
		Iterator<IoSession> itr = clients.iterator();
		while( itr.hasNext() ) {
			itr.next().write(log);
		}
	}

	@Override
	public boolean requiresLayout() {
		return true;
	}

	@Override
	public void close() {
		if(acceptor == null) return;
		acceptor.dispose();
		acceptor = null;
	}

    public static void main0(String[] args) {
    	/*
		加载日志配置文件，配置文件中必须有以下配置：

		log4j.rootLogger=DEBUG, LogServer, ...

		log4j.appender.Server=com.szwistar.common.comm.logserver.MinaLogServerAppender
		log4j.appender.Server.layout=org.apache.log4j.PatternLayout
		log4j.appender.Server.layout.ConversionPattern=%d{yy-MM-dd HH:mm:ss.SSS} %p %F:%L | %m%n
		log4j.appender.Server.port=9488
		log4j.appender.Server.encode=GB2312
		*/

		PropertyConfigurator.configure("log4j.properties");
		LOG.debug("测试应用启动.");
	}

    public static void main(String[] args) {
        org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger();
        root.setLevel(Level.DEBUG);

        Layout layout = new PatternLayout("%d{HH:mm:ss.SSS} %p %F:%L | %m%n");

        ConsoleAppender consoleAppender = new ConsoleAppender(layout, "System.out");
        root.addAppender(consoleAppender);

        MinaLogServerAppender socketAppender = new MinaLogServerAppender();
        socketAppender.setLayout(layout);
        root.addAppender(socketAppender);

		LOG.debug("测试应用启动.");
		
		final Logger LOG = LoggerFactory.getLogger("test");
		
		TimerEx.runEvery(1000, new Runnable() {
			@Override
			public void run() {		
				LOG.info("hello");
			}
		});
	}
}
